package org.Yiran.timetale_re.util.cooldown;

import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.world.level.saveddata.SavedData;
import net.minecraft.world.level.storage.DimensionDataStorage;
import net.minecraftforge.server.ServerLifecycleHooks;
import org.jetbrains.annotations.NotNull;

import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 世界冷却管理器
 * <p>
 * 管理当前世界中所有玩家的冷却数据，继承自SavedData以支持数据持久化。
 * 负责保存和加载所有玩家的冷却追踪器，并提供访问和管理这些追踪器的方法。
 * </p>
 * 
 * @author Yiran
 * @version 1.0
 */
public class WorldCooldownManager extends SavedData {
    
    // ==================== 常量定义 ====================
    
    /**
     * 数据文件名称
     * <p>
     * 用于标识保存在世界数据中的冷却管理器数据文件。
     * </p>
     */
    private static final String DATA_NAME = "timetale_re_cooldowns";

    // ==================== 数据存储 ====================
    
    /**
     * 玩家冷却追踪器映射
     * <p>
     * 以玩家UUID为键，冷却追踪器为值的映射，用于管理所有玩家的冷却数据。
     * </p>
     */
    private final Map<UUID, CooldownTracker> playerCooldowns = new HashMap<>();

    // ==================== 构造函数 ====================

    /**
     * 构造函数
     * <p>
     * 创建一个新的世界冷却管理器实例。
     * </p>
     */
    public WorldCooldownManager() {
        super();
    }

    // ==================== 实例获取方法 ====================

    /**
     * 获取世界冷却管理器实例
     * <p>
     * 从当前服务器的主世界数据存储中获取世界冷却管理器实例。
     * 如果当前没有运行的服务器，则返回null。
     * </p>
     * 
     * @return 世界冷却管理器实例，如果无法获取则返回null
     */
    public static WorldCooldownManager get() {
        if (ServerLifecycleHooks.getCurrentServer() == null) {
            return null;
        }

        DimensionDataStorage storage = ServerLifecycleHooks.getCurrentServer().overworld().getDataStorage();
        return storage.computeIfAbsent(WorldCooldownManager::load, WorldCooldownManager::new, DATA_NAME);
    }

    // ==================== 冷却追踪器管理方法 ====================

    /**
     * 获取指定玩家的冷却追踪器
     * <p>
     * 获取与指定玩家UUID关联的冷却追踪器。
     * 如果该玩家尚无冷却追踪器，则创建一个新的。
     * </p>
     * 
     * @param playerId 玩家UUID
     * @return 冷却追踪器
     */
    public CooldownTracker getCooldownTracker(UUID playerId) {
        return playerCooldowns.computeIfAbsent(playerId, CooldownTracker::new);
    }

    /**
     * 移除指定玩家的冷却追踪器
     * <p>
     * 从管理器中移除与指定玩家UUID关联的冷却追踪器。
     * 移除后标记数据为脏，以便在适当时候保存。
     * </p>
     * 
     * @param playerId 玩家UUID
     */
    public void removeCooldownTracker(UUID playerId) {
        playerCooldowns.remove(playerId);
        setDirty();
    }

    // ==================== NBT序列化方法 ====================

    /**
     * 保存数据到NBT标签
     * <p>
     * 将所有玩家的冷却追踪器数据序列化并保存到NBT标签中，
     * 以便持久化存储在玩家世界的保存数据中。
     * </p>
     * 
     * @param tag 要保存数据的NBT标签
     * @return 包含所有冷却数据的NBT标签
     */
    @Override
    public @Nonnull CompoundTag save(@NotNull CompoundTag tag) {
        ListTag playerList = new ListTag();

        for (Map.Entry<UUID, CooldownTracker> entry : playerCooldowns.entrySet()) {
            CompoundTag playerTag = new CompoundTag();
            playerTag.putUUID("PlayerId", entry.getKey());
            playerTag.put("CooldownData", entry.getValue().serializeNBT());
            playerList.add(playerTag);
        }

        tag.put("PlayerCooldowns", playerList);
        return tag;
    }

    /**
     * 从NBT标签加载数据
     * <p>
     * 从NBT标签中加载所有玩家的冷却追踪器数据，
     * 用于从世界保存数据中恢复冷却状态。
     * </p>
     * 
     * @param tag 包含冷却数据的NBT标签
     * @return 加载了数据的世界冷却管理器实例
     */
    public static WorldCooldownManager load(CompoundTag tag) {
        WorldCooldownManager manager = new WorldCooldownManager();

        ListTag playerList = tag.getList("PlayerCooldowns", Tag.TAG_COMPOUND);
        for (int i = 0; i < playerList.size(); i++) {
            CompoundTag playerTag = playerList.getCompound(i);
            UUID playerId = playerTag.getUUID("PlayerId");
            CooldownTracker tracker = new CooldownTracker(playerId);
            tracker.deserializeNBT(playerTag.getCompound("CooldownData"));
            manager.playerCooldowns.put(playerId, tracker);
        }

        return manager;
    }
}